package ar.com.sudoku.model;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import ar.com.sudoku.exceptions.InvalidSudokuException;

public class ForceBruteSudoku extends Sudoku {

	@Override
	public Set<Grid> solve() throws InvalidSudokuException {
		List<SudokuNumber> strategyList = Arrays.asList(SudokuNumber.values());
		Collections.shuffle(strategyList);
		boolean solve = false;
		if (grid.getValues().size() == 81) {
			if (validate()) {
				solve = true;
			}
		}
		Grid initialState = (Grid) grid.clone();
		int position = 0; 
		SudokuNumber x = SudokuNumber._1, y = SudokuNumber._1, number = strategyList.get(position);
		while (!solve) {
			while (!solve){
				while (!solve){
					if (grid.get(x,y) == null || initialState.get(x,y) == null) {
						grid.set(x,y,number);
						if (!this.validate()) {
							grid.set(x,y,null);
							if (SudokuNumber._1.equals(x) && SudokuNumber._1.equals(y) &&
									strategyList.get(8).equals(equals(number))) {
								throw new InvalidSudokuException("Unable to solve: " + initialState.toString());
							}
							if (!strategyList.get(8).equals(number)) {
								position++;
								number = strategyList.get(position);
							} else {
								SudokuNumber xBefore = x, yBefore = y, xActual = x, yActual = y;
								while (grid.get(xBefore,yBefore) == null || initialState.get(xBefore,yBefore) != null ||
										strategyList.get(8).equals(grid.get(xBefore,yBefore))) {
									if (strategyList.get(8).equals(grid.get(xBefore,yBefore)) && initialState.get(xBefore,yBefore) == null ) {
										grid.set(xBefore,yBefore,null);											
									}
									if (SudokuNumber._1.equals(yActual)) {
										if (SudokuNumber._1.equals(xActual)) {
											throw new InvalidSudokuException("Unable to solve: " + initialState.toString());
										}
										xBefore = xActual.previous();
										yBefore = SudokuNumber._9;
									} else {
										xBefore = xActual;
										yBefore = yActual.previous();
									}
									yActual = yBefore;
									xActual = xBefore;
								}

								x = xBefore;
								y = yBefore;
								SudokuNumber numberBefore = grid.get(xBefore,yBefore);
								position = strategyList.indexOf(numberBefore);
								position++;
								number = strategyList.get(position);
							}
						} else {
							if (grid.getValues().size() == 81) {
								solve = true;
							} else {
								position = 0;
								number = strategyList.get(position);
								if (SudokuNumber._9.equals(y)) {
								    x = x.next();
								    y = SudokuNumber._1;
							    } else {
									y = y.next();						    	
							    }
							}
						}
					} else {
						if (SudokuNumber._9.equals(y)) {
						    x = x.next();
						    y = SudokuNumber._1;
					    } else {
					    	y = y.next();	
					    }
					}
				}
				if (!solve) {
					if (SudokuNumber._9.equals(y)) {
						position = 0;
						number = strategyList.get(position);
					    x = x.next();
					    y = SudokuNumber._1;
				    }
				}
			}
			if (!solve) {
				position = 0;
				number = strategyList.get(position);
				y = y.next();
			}
		}
		for (SudokuNumber n : grid.getValues()) {
			if (n == null) {
				throw new InvalidSudokuException("Unable to solve: " + initialState.toString());
			}
		}
		Set<Grid> grids = new HashSet<Grid>();
		grids.add(grid);
		return grids;
	}
}
